
;*******************************************************
;
;	SCSI Driver 'Write' filter.
;
;	Written by Matt Gulick.		Started June 14,1988
;
;	Copyright Apple Computer, Inc. 1988-90
;
;*******************************************************

;*******************************************************
;
;	This file contains the 'Write' filter as defined in
;	the ERS.
;
;*******************************************************

;*******************************************************
;
;	Revision History:
;
;*******************************************************

;	June 14,	1988	File started.
;	June 21,	1988	Modified to mach the results of
;						the Code Review and added
;						register comments.

				STRING		PASCAL
				BLANKS		OFF
				PAGESIZE	70
				PRINT		NOGEN
				PRINT		NOMDIR
				MACHINE		M65816

				IMPORT		unit_state
				IMPORT		test_unit_rdy
				IMPORT		auto_sense_data
				IMPORT		main_drvr
				IMPORT		call_type
				IMPORT		w_update_cache
				IMPORT		divend
				IMPORT		divsor
				IMPORT		result
				IMPORT		max_blk_cnt
				IMPORT		divide
				IMPORT		check_532_rw

				PRINT		OFF

				INCLUDE		'scsihd.equates'
				INCLUDE		'M16.MEMORY'
				INCLUDE		'M16.UTIL'
				PRINT		ON

				EJECT
			
;*******************************************************
;
;	Main Entry point to the 'Write' filter.  This
;	"Filter" takes the information given by the caller
;	on direct page and builds the equivilent to a Write
;	Extended Control Call.  In order of appearence:
;
;			Verify that Device #		$0000
;			Call Number				=	$0003
;			Block Size				=	dib.blksize
;
;	We now Build the SCSI Main Driver Command and send
;	it.
;
;	The following will be validated by the Main Driver
;	when it builds the command. 
;
;			Request Count			=	Block Size * i
;			Block Number			=	Blk Num + Offset
;				This is for partitions.
;
;	After calling the Main driver and if no errors were
;	encountered, then the Transfer count will be
;	updated.
;
;	Inputs:		None.
;
;	Outputs:	Acc			=	0
;				Carry		=	0
;				Y register	=	Unspecified
;				X register	=	Unspecified
;				P register	=	0=M=X=e
;				Direct Page	=	GS/OS Direct Page
;				Data Bank	=	Ours
;
;	Errors:		See Spec.
;
;*******************************************************

				EXPORT	Write
Write			PROC

;-------------------------------------------------------------------------------

				IF			scsi_dtype = apple_cd	THEN
												;
												; Device is not writable.
												;
				lda		#drvr_wrt_prot
				sec
				rts

				ENDIF

;-------------------------------------------------------------------------------

				IF			scsi_dtype <> apple_cd	THEN

				stz		@cache
												;
												; Is the device Removable?
												;
				ldy		#dib.dvcchar

				lda		[dib_ptr],y
				and		#removable
				beq		@write					;No.
												;
												; This device is removable.  Now we
												; need to check to see if the unit
												; has gone offline, (then we need to
												; report that to the OS) or if the
												; unit has come back online (Rebuild
												; the DIBs).
												;
				jsr		unit_state
				bcs		@rts_out
												;
												; Is the device online?
												;
				ldy		#dib.dvcflag

				lda		[dib_ptr],y
				and		#dvc_hardofl
				bne		@off_line				;Yes.

				lda		[dib_ptr],y
				and		#dvc_online
				bne		@write					;Yes.
												;
												; Device is currently offline.
												;
@off_line		lda		#drvr_off_line
				sec
@rts_out		rts
												;
												; Let's check the request count.  If
												; this is $00000000, then exit clean
												; with no data transfered.
												;
@write			lda		<rqst_cnt
				and		#$01ff					;Cheap Check for multiple of 512
				bne		@bad_rqst_cnt
				ora		<rqst_cnt+2
				bne		@chk_max
				jmp		@completed
												;
												; Block Number is beyond the range for this disk.
												;
@out_of_range	lda		#drvr_bad_blk
				sec
				rts
												;
												; Requested Byte Count not a multiple of 512
												;
@bad_rqst_cnt	lda		#drvr_bad_cnt
				sec
				rts
												;
												; Check to see if more is being
												; requested then we can give him.
												;
@chk_max		lda		<rqst_cnt
				sta		|divend
				lda		<rqst_cnt+2
				sta		|divend+2

				lda		#block_size
				sta		|divsor

				sec
				ldy		#dib.blkcnt
				lda		[dib_ptr],y
				sbc		<block_num
				sta		|max_blk_cnt
				ldy		#dib.blkcnt+2
				lda		[dib_ptr],y
				sbc		<block_num+2
				sta		|max_blk_cnt+2
				bcc		@out_of_range

				jsr		divide
				bcs		@rts_now

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				IF			scsi_dtype = direct_acc	THEN

												;
												; Verify Block Size.
												;
@cnt_non_zero	ldy		#dib.blksize
				lda		[dib_ptr],y				;Block Size
				cmp		<blk_size
				bne		@chk_532

				ldy		#dib.blksize+2
				lda		[dib_ptr],y
				beq		@blk_size_ok

@bad_parm		lda		#drvr_bad_parm
				sec
@rts_now		rts
												;
												; Check for 532 byte block size
												;
@chk_532		tax

				lda		<blk_size
				cmp		#block_size
				bne		@bad_parm

				cpx		#$0214
				beq		@no_cache
				bra		@bad_parm

				ELSE

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

												;
												; Verify Block Size.
												;
@cnt_non_zero	ldy		#dib.blksize
				lda		[dib_ptr],y				;Block Size
				cmp		<blk_size
				bne		@bad_parm

				ldy		#dib.blksize+2
				lda		[dib_ptr],y
				beq		@blk_size_ok

@bad_parm		lda		#drvr_bad_parm
				sec
@rts_now		rts

				ENDIF

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@blk_size_ok

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				IF			cache_blks = true	THEN
												;
												; Regardless of the Cache Priority,
												; we need to see if it is in the cache.
												;
												; Set flag to cache the block only if
												; it is already there.
												;
				dec		@cache

				lda		<cache_prio				;Deferred Mode?
				bmi		@deferred				;Yes.

				ENDIF

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@no_cache
												;
												; Build the (Write Data) Status
												; Command $802A, and just in case
												; it is not accepted, we will also
												; build $800A.
												;
				lda		<block_num				; Sent to me Low >> High.
				xba								; I Send it out High >> Low.
				sta		|c_block_num_l+2
				sta		|c_block_num_s
				lda		<block_num+2
				xba	
				sta		|c_block_num_l
												;
												; Set Main Driver Pointer to
												; our data for the command.
												;
				lda		#cmd_$802A
				sta		<scsi_mdrvr
				lda		#^cmd_$802A
				sta		<scsi_mdrvr+2
												;
												; Call Main Driver
												;
				lda		#scsit_cont
				sta		|call_type

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				IF			scsi_dtype = direct_acc	THEN
												;
												; Issue the call.
												;
				jsr		check_532_rw
				bcs		@munge
@deferred		jmp		@completed
												;
												; At this point the extended call was not
												; excepted.  We must now issue the normal
												; version of this command.  First, we must
												; check to see if the request is within the
												; one byte block count range.  If not, we
												; will need to special process this request.
												;
												; Setup Divide routine while preserving the
												; origonal count for later.
												;
@munge			lda		<rqst_cnt
				sta		@orig_rqst
				sta		|divend
				lda		<rqst_cnt+2
				sta		@orig_rqst+2
				sta		|divend+2

				lda		<blk_size
				sta		|divsor

				jsr		divide
				bcc		@do_it
				rts
												;
												; Preserve Buffer pointer.  We will need to
												; bump it a few times.
												;
@do_it			lda		<buff_ptr
				sta		@orig_buff
				lda		<buff_ptr+2
				sta		@orig_buff+2
												;
												; Get resultin block count.  If > a
												; single byte count, then we need to
												; break it into multiple calls.  If
												; not, then send it as is.
												;
				lda		|result
				sta		@blk_rqst
				lda		|result+1
				sta		@blk_rqst+1
				beq		@issue_call
												;
												; Set the max count for a single byte block
												; count (Block size * $100).
												;
				lda		<blk_size-1
				and		#$ff00
				sta		<rqst_cnt
				lda		<blk_size+1
				and		#$00ff
				sta		<rqst_cnt+2
												;
												; Set Main Driver Pointer to
												; our data for the command.
												;
@issue_call		lda		#cmd_$800A
				sta		<scsi_mdrvr
				lda		#^cmd_$800A
				sta		<scsi_mdrvr+2
												;
												; Call Main Driver
												;
				lda		#scsit_cont
				sta		|call_type
												;
												; Issue the call.
												;
				jsr		check_532_rw
				bcc		@did_munge
				sta		@error_loc
				lda		auto_sense_data+\
						rqst_sens.sense_key
				and		#$00ff					;Checking for $03
				cmp		#$0003					;This covers $03
				bne		@craped_out
												;
												; Some kind of I/O Error.
												;
@io_error		lda		#drvr_io
				sec
				rts

@craped_out		lda		@error_loc
				sec
@rts_0			rts

@error_loc		dc.w	null
												;
												; Check for special processing.
												;
@did_munge		dec		@blk_rqst+1
				bmi		@done					;All Done
				php
												;
												; Bump values for the next call.
												;
				clc
				lda		<rqst_cnt
				adc		<buff_ptr
				sta		<buff_ptr
				lda		<rqst_cnt+2
				adc		<buff_ptr+2
				sta		<buff_ptr+2
												;
												; Is this the last partial?
												;
				plp
				bne		@over					;Yes.
												;
												; Get remaining bytes to be transfered.
												;
				sec
				lda		@orig_rqst
				sbc		<rqst_cnt
				sta		<rqst_cnt
				lda		@orig_rqst+2
				sbc		<rqst_cnt+2
				sta		<rqst_cnt+2
				ora		<rqst_cnt
				beq		@done
												;
												;*****************************************
												;* Bump Block number.  Pay close         *
												;* attention to what this comment says.  *
												;* If you don't and you change the code  *
												;* then your on your own.  This word is  *
												;* in MSB >> LSB order.  We need to add  *
												;* $100 to it.  This is done by a simple *
												;* increment.                            *
												;*****************************************
												;
@over			inc		|c_block_num_s

				bra		@issue_call
												;
												; Restore the environment.
												;
@done			lda		@orig_rqst
				sta		<rqst_cnt
				lda		@orig_rqst+2
				sta		<rqst_cnt+2

				lda		@orig_buff
				sta		<buff_ptr
				lda		@orig_buff+2
				sta		<buff_ptr+2


				ELSE

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				jsr		|main_drvr
				bcc		@completed

				lda		#drvr_io
				rts								;There was an error!

@deferred

				ENDIF

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

												;
												; Update Transfer Count.
												;
@completed		lda		<rqst_cnt

;-------------------------------------------------------------------------------

				IF			block_dvc = true\
				AND			character_dvc = false		THEN

				and		#%1111111111111110

				ENDIF							;block_dvc = true

;-------------------------------------------------------------------------------

				sta		<trans_cnt
				lda		<rqst_cnt+2
				sta		<trans_cnt+2

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

				IF			cache_blks = true	THEN
												;
												; Check if cache bypassed.
												;
				bit		@cache
				bpl		@out_of_here			;Yes.

				clc
				lda		<cache_prio				;Deferred Mode?
				bmi		@chk_cache				;Yes.

				bne		@chk_cache				;No.

				sec								;Update only
@chk_cache		jsr		w_update_cache
				bcc		@out_of_here
				lda		<cache_prio				;Deferred Mode?
				bpl		@out_of_here			;No
				stz		@cache					;Yes. Force write skipping the cache
				jmp		@no_cache				;logic this time around.

				ENDIF

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

												;
												; Exit No Error.
												;
@out_of_here	lda		#$0000
				clc
@rts			rts

												;
												; Variables and storage for short call.
												;
@cache			dc.w	null					;Cache flag
@do_532			dc.w	null					;532 byte block flag

@orig_buff		dc.l	null					;Origonal Buffer Pointer
@orig_rqst		dc.l	null					;Origonal Request Count
@blk_rqst		dc.l	null					;Number of Blocks requested
												;
												; Command Data for this call.
												;
cmd_$800A		dc.b	$0A
				dc.b	$00
c_block_num_s	dc.w	$0000
c_block_cnt_s	dc.b	$00
				dcb.b	7,$00
												;
												; Command Data for this call.
												;
cmd_$802A		dc.b	$2A
				dc.b	$00
c_block_num_l	dc.l	$00000000
c_block_cnt_l	dcb.b	3,$00
				dcb.b	7,$00

				ENDIF

;-------------------------------------------------------------------------------

				IF			character_dvc = true\
				AND			block_dvc = false		THEN

													;
													; Check to see if the Device is Opened.
													;
				lda		|open_flag
				beq		@not_open
													;
													; Is the device online?
													;
@open
				ldy		#dib.dvcflag

				lda		[dib_ptr],y
				and		#dvc_hardofl
				bne		@off_line					;Yes.

				lda		[dib_ptr],y
				and		#dvc_online
				bne		@write						;Yes.
													;
													; Device is currently offline.
													;
@off_line		lda		#drvr_off_line
				sec
@rts_out		rts
													;
													; Device is not Opened.
													;
@not_open		lda		#drvr_not_open
				sec
				rts

@write

;-------------------------------------------------------------------------------

				IF			scsi_dtype = character_dvc	THEN

												;
												; Save Requested amount for later.
												;
				lda		<rqst_cnt
				sta		@t_rqst_cnt				;Total Request Count (LOW)
				sta		@r_rqst_cnt				;Remaining Request Count (LOW)
				lda		<rqst_cnt+2
				sta		@t_rqst_cnt+2			;Total Request Count (HIGH)
				sta		@r_rqst_cnt+2			;Remaining Request Count (HIGH)

				lda		<buff_ptr
				sta		@orig_buffer
				lda		<buff_ptr+2
				sta		@orig_buffer+2

												;
												; Clear out Total Data Transfered Area.
												;
				stz		@t_transfered
				stz		@t_transfered+2
												;
												; Place your Write Code Here.
												;
			This space for rent.
												;
												; Clean Exit.
												;
				lda		#$0000
				clc
				rts
										
												; 
												; Data Area.
												;
@orig_buffer	dc.l	null					;Origonal Buffer Pointer
@t_rqst_cnt		dc.l	null					;TOTAL REQUEST COUNT
@r_rqst_cnt		dc.l	null					;REMAINING REQUEST COUNT
@t_transfered	dc.l	null					;AMOUNT ACTUALLY TRANSFERED
@enough_data	dc.w	null					;Is there enough data flag.
@stuff			dc.w	null					;Temporary Storage
												;
												; Command Description.
												;
cmd_$8028		dc.b	$28
rel_adr			dc.b	null					;Relative Address Bit is bit 0
t_data_type		dc.b	null					;Transfer Data Type
				dc.b	null					;Reserved
t_id			dc.w	null					;Transfer ID
t_length		dcb.b	3,null					;Transfer Length
control			dc.b	null					;Control Byte

				ENDIF

;-------------------------------------------------------------------------------

				ENDIF

;-------------------------------------------------------------------------------


				ENDP

				EJECT
			
				END
